package com.huishu.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.huishu.commons.exception.BaseException;
import com.huishu.gateway.utils.AesEncrypt;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class ReqFilter implements GlobalFilter, Ordered {


    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                if (contentLength > 0L) {
                    httpHeaders.setContentLength(contentLength);
                } else {
                    httpHeaders.set("Transfer-Encoding", "chunked");
                }
                return httpHeaders;
            }

            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }


    private Mono<Void> returnMononew(GatewayFilterChain chain, ServerWebExchange exchange) {
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }

    private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 重新构造request，参考ModifyRequestBodyGatewayFilterFactory
        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
        String params = exchange.getRequest().getHeaders().getFirst("X-Params");
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        log.info("mediaType:{}", JSON.toJSONString(mediaType));
        // 重点
        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
            // 因为约定了
            // 终端传参的格式，所以只考虑json的情况，如果是表单传参，请自行增加
            if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType)) {
                String newBody = null;
                try {
                    // 解密body 此处调用你自己的解密方法
                    log.info("body:{}", body);
                    newBody = AesEncrypt.aesDecrypt(body);
                } catch (Exception e) {
                    log.error("解密失败尝试解密请求头中params：{}", e.getMessage());
                    try {
                        newBody = AesEncrypt.aesDecrypt(params);
                    } catch (Exception e1) {
                        log.error("请求头中params也解密失败：{}", e1.getMessage());
                        throw new BaseException("参数解密失败");
                    }
                }
                return Mono.just(newBody);
            }
            return Mono.empty();
        });
        BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        // 猜测这个就是之前报400错误的元凶，之前修改了body但是没有重新写content length
        headers.remove("Content-Length");
        // MyCachedBodyOutputMessage 这个类完全就是CachedBodyOutputMessage，只不过CachedBodyOutputMessage不是公共的
        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
            ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage);
            return returnMononew(chain, exchange.mutate().request(decorator).build());
        }));
    }


    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        log.info("HttpMethod:{},Url:{}", request.getMethod(), request.getURI().getRawPath());
        // 处理参数
        MediaType contentType = headers.getContentType();
        long contentLength = headers.getContentLength();
        if (contentLength > 0) {
            if (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) {
                return readBody(exchange, chain);
            }
        }
        return chain.filter(exchange);

    }

    @Override
    public int getOrder() {
        return 2;
    }
}
